﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using BomberManServer.Utils;
using States = BomberManServer.Gaming.Object.Mapping.Model.States;

namespace BomberManServer.Gaming.Object.Mapping
{
    public abstract class Map : InGame
    {
        public Graph<Block> blocks { get; private set; }
        private Model.Map model;

        private Logger.Handler log;
        private Random seed_generator;
        

        #region Init

        public Map(Game game, Model.Map model)
            : base(game)
        {
            this.game = game;
            this.model = model;
            // instanciate all Items
            seed_generator = new Random();


            blocks = new Graph<Mapping.Block>(model.blocks.width, model.blocks.height);
            foreach (int index in Enumerable.Range(0, blocks.data.Length))
            {
                Block block = new Block((uint)(index % model.blocks.width), (uint)(index / model.blocks.width));
                blocks.data[index] = block;
            }
            log = game.logger.createHandler("Map");
            log.information("Instanciated");
        }

        // regenerate the map from the model
        public void reset()
        {
            // generate new seed for this round
            uint seed = (uint)seed_generator.Next();
            log.information("seed : " + seed);
            game.seed = seed;
            model.construct(blocks);
            log.information("Reset.");
            game.sender.onMapReset(seed);
            this.resetPlayer();
        }

        // reset all player to default settings
        public void resetPlayer()
        {
            foreach (Player player in this.game.match.players)
            {
                player.avatar.modBonus.reset();
                player.avatar.isAlive = true;
                player.avatar.bombs.Clear();
            }

        }

        #endregion Init

        #region Operations

        // tries to move the avatar in the direction
        // return true if avatar was moved 
        public bool moveAvatar(Items.Avatar avatar, Direction direction)
        {
            //log.information("Avatar " + avatar.id + " is trying to move");
            if (avatar.isAlive)
            {
                if (avatar.isPlantingBomb)
                {
                    if (plantBomb(avatar, direction))
                    {
                        game.sender.onBombSet(avatar.bombs.Last());
                        game.sender.onPlayerMove(avatar.player);
                    }

                }
                else
                {
                    // new coordinates
                    Coordinates coord = avatar.coord.translate(direction, avatar.speed);
                    Coordinates[] avatarCoord = avatarCoords(coord);

                    // position backup
                    List<Block> affecteds = new List<Block>();

                    // if coordinates are ok
                    foreach (Coordinates coordSide in avatarCoord)
                        if (!blocks.check(coordSide))
                            return false;
                        else
                            if (!affecteds.Contains(blocks.at(coordSide)))
                                affecteds.Add(blocks.at(coordSide));

                    //save avatar current blocks
                    List<Block> current = new List<Block>(avatar.blocks);

                    // detach avatar of the map
                    avatar.detach();

                    if (affecteds.Count > 2)
                    {
                        // glide function : help move
                        if (avatar.coord.glideable(direction, this, avatarCoord))
                        {
                            avatar.coord = coord.glide(direction, avatar);

                            //TODO
                            // gérer les nouveaux blocs de l'avatar (possibe que 1 de moins)
                            return true;
                        }
                        else
                        {

                            avatar.attach(current);
                            return avatar.coord.hugSide(direction);
                        }
                    }
                    else
                    {
                        string truc;
                        // check if new positions are ok
                        foreach (Block block in affecteds)
                        {
                            // if not ok
                            if (!block.item.free)
                            {
                                if (block.item.GetType().Name == "Bomb")
                                    block.item.actionWalk(block, avatar);
                                // restore old position
                                if (avatar.isAlive)
                                    avatar.attach(current);
                                //log.information("Avatar stay on the block");
                                return avatar.coord.hugSide(direction);
                            }

                        }

                        // attach to new positions and react
                        if ((coord.xBlock != avatar.coord.xBlock) || (coord.yBlock != avatar.coord.yBlock))
                        {
                            avatar.lastBlockCoord = Coordinates.sidedCoord(avatar.coord, direction);
                        }

                        avatar.coord = coord;
                        avatar.attach(affecteds);
                        if (avatar.isAlive)
                            log.information("Avatar is moved to (" + coord.xPoint + ", " + coord.yPoint + ")");
                        return true;
                    }
                }
            }
            else
                log.information("Avatar is dead so stfu and be still like a good corpse");
            return false;
        }

        // plant a bomb on the map
        public bool plantBomb(Items.Avatar avatar,Direction dir)
        {
            Block newBlock = this.blocks.at(avatar.coord.getNextBlockCoord(dir));
            //check new block in the dir
            if((!newBlock.item.free) && !(newBlock.item == avatar))
                return false;
            
            Coordinates coordBomb = new Coordinates(avatar.coord);
            Coordinates coordAvatar = newBlock.coordinates;
            avatar.detach();

            avatar.coord = coordAvatar;
            avatar.attach(newBlock);
            avatar.plantBomb(blocks.at(coordBomb), coordBomb);
            log.information("Bomb has been planted on (" + coordBomb.xPoint + ", " + coordBomb.yPoint + ")");
            log.information("Avatar Moved due to bomb to (" + avatar.coord.xPoint + ", " + avatar.coord.yPoint + ")");
            game.sender.onPlayerMove(avatar.player);
            return true;
            
            
            
        }

        // switch the avatar's position to the block at the specified coordinates
        public void gotoBlock(Items.Avatar avatar ,Coordinates coord)
        {
            Direction direction = coord.getDirectionTo(avatar.coord);
            avatar.detach();
            avatar.coord = coord;
            avatar.attach(blocks.at(coord));
            avatar.coord.hugSide(direction);
            log.information("Avatar is moved to (" + coord.xPoint + ", " + coord.yPoint + ")");
        }

        #endregion Operations

        #region Utils

        public Block getBlockAt(Coordinates coord)
        {
            return blocks.at(coord);
        }

        public Coordinates[] avatarCoords(Coordinates coord)
        {
            Coordinates[] coords = new Coordinates[4];
            coords[0] = coord.translate(Direction.UP, Constants.instance.avatarSize * Constants.instance.pointsPerBlock);
            coords[1] = coord.translate(Direction.DOWN, Constants.instance.avatarSize * Constants.instance.pointsPerBlock);
            coords[2] =  coord.translate(Direction.LEFT, Constants.instance.avatarSize * Constants.instance.pointsPerBlock);
            coords[3] =  coord.translate(Direction.RIGHT, Constants.instance.avatarSize * Constants.instance.pointsPerBlock);

            return coords;
        }

        public bool exists(uint x, uint y)
        {
            return blocks.check(x, y);
        }

        #endregion Utils

    }
}